using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using Microsoft.Scripting;
using Microsoft.Scripting.Internal.Generation;
using Microsoft.Scripting.Actions;

namespace Nua.Runtime
{
    public class NuaBinder : ActionBinder
    {
        public static readonly ActionBinder Default = new NuaBinder(NuaContext.CreateDefaultContext(null));

        // TODO://
        public NuaBinder(CodeContext context) : base(context)
        {
        }

        protected override StandardRule<T> MakeRule<T>(Action action, object[] args)
        {
            switch (action.Kind) 
            {
                case ActionKind.DoOperation:
                    return new DoOperationBinderHelper<T>(this, Context, (DoOperationAction)action).MakeRule(args);
                case ActionKind.GetMember:
                    //return new GetMemberBinderHelper<T>(this, (GetMemberAction)action).MakeNewRule(args);
                case ActionKind.SetMember:
                    //return new SetMemberBinderHelper<T>(this, (SetMemberAction)action).MakeNewRule(args);
                case ActionKind.Call:
                    //return new CallBinderHelper<T>(this).MakeRule(Context, (CallAction)action, args);
                default:
                    throw new NotImplementedException(action.ToString());
            }
        }

        public override void EmitConvertFromObject(CodeGen cg, Type toType)
        {
            if (toType == typeof(object)) return;

            if (toType == typeof(NuaTable))
            {
                cg.Emit(OpCodes.Castclass, toType);
                return;
            }
            
            MethodInfo fastConvertMethod = GetFastConvertMethod(toType);
            if (fastConvertMethod != null) {
                cg.EmitCall(fastConvertMethod);
                return;
            }
          
            if (toType == typeof(void)) {
                cg.Emit(OpCodes.Pop);
            } 

            throw new Exception("not implemented");
        }

        private static MethodInfo GetFastConvertMethod(Type toType) 
        {
            if (toType == typeof(bool)) {
                return typeof(Converter).GetMethod("ConvertToBoolean");
            }

            if (toType == typeof(double)) {
                return typeof(Converter).GetMethod("ConvertToDouble");
            }

            return null;
        }

        public override object Convert(object obj, Type toType)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override bool CanConvertFrom(Type fromType, Type toType, NarrowingLevel level)
        {
            if (toType == fromType) return true;
            if (toType.IsAssignableFrom(fromType)) return true;
            if (fromType.IsCOMObject && toType.IsInterface) return true; // A COM object could be cast to any interface


            throw new NotImplementedException();
        }

        public override bool PreferConvert(Type t1, Type t2)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override Microsoft.Scripting.Internal.Ast.Expression ConvertExpression(Microsoft.Scripting.Internal.Ast.Expression expr, Type toType)
        {
           Type exprType = expr.ExpressionType;

            //if (toType == typeof(object)) {
            //    if (exprType.IsValueType) {
            //        return StaticUnaryExpression.Convert(expr, toType);
            //    } else {
            //        return expr;
            //    }
            //}

            if (toType.IsAssignableFrom(exprType)) {
                return expr;
            }

            Microsoft.Scripting.Internal.Ast.BoundExpression be = expr as Microsoft.Scripting.Internal.Ast.BoundExpression;
            if (be != null && be.Reference.KnownType != null) {
                if (toType.IsAssignableFrom(be.Reference.KnownType)) {
                    return Microsoft.Scripting.Internal.Ast.StaticUnaryExpression.Convert(expr, toType);
                }
            }

            //// We used to have a special case for int -> double...
            //if (exprType != typeof(object)) {
            //    expr = StaticUnaryExpression.Convert(expr, typeof(object));
            //}

            //if (toType == typeof(object)) return expr;

            //MethodInfo fastConvertMethod = GetFastConvertMethod(toType);
            //if (fastConvertMethod != null) {
            //    return MethodCallExpression.Call(null, fastConvertMethod, expr);
            //}


            throw new Exception("The method or operation is not implemented.");
        }
    }
}
